package de.koller.aframe.util.noise.n3D;

import de.koller.aframe.util.math.Function3DPreview;
import java.util.Random;

public class Perlin3D extends Noise3D {
	public static boolean PRINT_PROGRESS = false;
	
	private final Random rnd;
	private final float persistence;
	private final float frequenceFactor;
	private final int steps;
	private final int startStep;
	
	public Perlin3D( int startStep, int steps, float persistence, float frequenceFactor, Random rnd ) {
		super( noiseArray( startStep+steps ) );
		this.rnd = rnd;
		this.startStep = startStep;
		this.steps = steps;
		this.persistence = persistence;
		this.frequenceFactor = frequenceFactor;
		
		this.generate();
	}
	
	private void generate() {
		for( int i = startStep; i < steps+startStep; i++ )
			handleStep( i );
	}
	
	private void handleStep( int step ) {
		float amplitude = (float)Math.pow( persistence, step );
		int frequence = (int)Math.pow( frequenceFactor, step );
		
		Noise3D n = new Noise3D( rnd, frequence + 3 );

		for( int ax = 0; ax < noise.length; ax++ ) {
			for( int ay = 0; ay < noise.length; ay++ ) {
				for( int az = 0; az < noise.length; az++ ) {
					float x = 1f / (float)noise.length * ax;
					float y = 1f / (float)noise.length * ay;
					float z = 1f / (float)noise.length * az;
				
					noise[ax][ay][az] += n.getValue( x, y, z ) * amplitude;
				}
			}
			if( PRINT_PROGRESS && ax % 100 == 0 ) {
				String progress = "berechnen: " + step + " / " + (steps+startStep) + 
						" size: " + ax + " / " + noise.length;
				System.out.println( progress );
			}
		}
		
	}
	
	private static float[][][] noiseArray( int steps ) {
		int s = (int)Math.pow( 2, steps ) + 3;
		return new float[ s ][ s ][ s ];
	}
	
	public static float maxHeight( int startDetail, int details, float persistence ) {
		double h = 0;
		for( int i = startDetail; i < startDetail+details; i++ )
			h += Math.pow( persistence, i );
		return (float)h;
	}
	
	public static void main( String[] args ) {
		Random r = new Random();
		
		Perlin3D.PRINT_PROGRESS = true;
		
		Perlin3D n = new Perlin3D( 0, 2, 0.6f, 2, r );
		n.normalize( 1 );
		
		Function3DPreview.STEP_INTERVAL = 50;
		Function3DPreview.Z_STEPS = 1000;
		Function3DPreview.PRINT_PROGRESS = true;
		Function3DPreview.showFunction( 0, 0, 0, 1, 1, 1, 0, 1, n );
	}
	
}
